package lzupdater.net;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.CookieHandler;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.util.List;

import lzupdater.com.CommunicationHub;
import lzupdater.com.HubComponent;
import lzupdater.gui.DebugPanel;
import lzupdater.locale.LocalizedString;
import lzupdater.log.Logger;
import lzupdater.utils.Encrypter;
import lzupdater.utils.Profile;
import lzupdater.utils.StatusItem;
import lzupdater.utils.StatusItemManager;

//LZRosterUpdater, a java client to update LZRoster
//Copyright (C) 2008 Fraskor from Krasus (e-mail: fraskor@gmail.com)
//
//This file is part of LZRosterUpdater.
//
//LZRosterUpdater is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

/**
 * 
 * @author Fraskor
 */
public class LZUpdater extends HubComponent {

	private final static LocalizedString startUpdateWith = new LocalizedString(
			"Start update with :");
	private final static LocalizedString loginInfo = new LocalizedString(
			"login    = %s");
	private final static LocalizedString passwordInfo = new LocalizedString(
			"password = %s");
	private final static LocalizedString fileInfo = new LocalizedString(
			"file     = %s");
	private final static LocalizedString addressInfo = new LocalizedString(
			"address  = %s");

	private final static LocalizedString getPHPSessionId = new LocalizedString(
			"Connect to '%s' to get the PHP session ID");
	private final static LocalizedString fillIdentificationForm = new LocalizedString(
			"Connect to '%s' for identification");
	private final static LocalizedString noPHPSESSIDCookieFound = new LocalizedString(
			"No PHPSESSID cookie found in the response from '%s' !!");
	private final static LocalizedString checkTheUrlOfTheRoster = new LocalizedString(
			"Please check the URL of the roster");
	private final static LocalizedString couldNotGetPHPSESSID = new LocalizedString(
			"Could not get the PHPSESSID");
	private final static LocalizedString invalidLoginOrPassword = new LocalizedString(
			"Login and/or password is/are invalid");
	private final static LocalizedString identificationRejected = new LocalizedString(
			"The identification has been rejected !");
	private final static LocalizedString sendLZRosterData = new LocalizedString(
			"Send data file to '%s'");
	private final static LocalizedString updateCompleted = new LocalizedString(
			"Update completed");
	private final static LocalizedString malformedUrlAddress = new LocalizedString(
			"Roster URL is invalid '%s'");

	private final static LocalizedString unknownError = new LocalizedString(
			"Unknown error");
	private final static LocalizedString errorWhenConnection = new LocalizedString(
			"An error occured during the connection process: %s");
	private final static LocalizedString pleaseWait = new LocalizedString(
			"Please wait");
	private final static LocalizedString listOfUpdatedCharacters = new LocalizedString(
			"List of updated characters : ");
	private final static LocalizedString noCharacterUpdated = new LocalizedString(
			"No character updated !");
	private final static LocalizedString disconnetFromRoster = new LocalizedString(
			"Logout from roster");
	private final static LocalizedString unknownHost = new LocalizedString(
			"Unknown host '%s'");
	private final static LocalizedString inputDataFileNotFound = new LocalizedString(
			"The LZProfile.lua data file could not be found");

	private HTTPData data = null;

	private StatusItemManager statusItemManager = null;
	private DebugPanel debugPanel;
	
	
	public LZUpdater(CommunicationHub hub) {
		super(hub);
	}

	@Override
	public void initializeCommunication() {
		debugPanel = hub.getComponent(DebugPanel.class);
	}

	public void updateRoster(Profile parametres, Logger log) {

		String rosterAddress = parametres.getRosterUrl();

		if (!rosterAddress.endsWith("/")) {
			rosterAddress += "/";
		}

		log.info(startUpdateWith);
		log.info("\t" + loginInfo.useAsFormater(parametres.getLogin()));
		log.info("\t"
				+ passwordInfo.useAsFormater(Encrypter
						.getMaskedPassword(parametres.getPassword())));
		log.info("\t" + fileInfo.useAsFormater(parametres.getRosterFile()));
		log.info("\t" + addressInfo.useAsFormater(rosterAddress));

		String addressDisconnection = rosterAddress + "disconnection.php";
		String addressConnection = rosterAddress + "connection.php";
		String addressSend = rosterAddress + "send.php";
		String addressLanguage = rosterAddress + "change_language.php?l=enUS";

		PostData postData = null;

		Cookies cookieIdSession = null;

		// Disable any cookie handler (JWS setup a default cookie handler that
		// conflict with
		// the internal cookie manager of LZRosterUpdater)
		CookieHandler.setDefault(null);

		// First check if the address is correct
		HTTPClient.setPerformRedirection(false);
		HTTPClient.setThrower(false);
		data = HTTPClient.connect(addressDisconnection, log);
		debugPanel.addHtmlPage(data);
		checkAndThrowError(data, log);

		cookieIdSession = data.getCookies().extract("PHPSESSID");

		if (cookieIdSession.getCookie("PHPSESSID") == null) {
			cookieIdSession = null;
		}

		HTTPClient.setPerformRedirection(true);
		HTTPClient.setThrower(false);

		// First connection to check the URL and get the id of the PHP Session
		log.info("1 - " + getPHPSessionId.useAsFormater(addressConnection));
		// Use disconnection address to be sure to be disconnected. This will
		// redirect to the connection
		// page at the end
		data = HTTPClient.connect(addressConnection, cookieIdSession, log);
		debugPanel.addHtmlPage(data);
		checkAndThrowError(data, log);

		// Check the URL from its HTML source
		if (data
				.getSource()
				.contains(
						"form action=\"connection.php\" method=\"post\" id=\"formconnect\" class=\"send\"") < 0) {
			log.error(malformedUrlAddress.useAsFormater(rosterAddress));
			log.error(checkTheUrlOfTheRoster);
			throw new RuntimeException(malformedUrlAddress
					.useAsFormater(rosterAddress));
		}

		// Get the information about the id session
		if (cookieIdSession == null)
			cookieIdSession = data.getCookies().extract("PHPSESSID");

		if (cookieIdSession.getCookie("PHPSESSID") == null) {
			log.error(noPHPSESSIDCookieFound.useAsFormater(addressConnection));
			log.error(checkTheUrlOfTheRoster);
			throw new RuntimeException(couldNotGetPHPSESSID.toString());
		}

		// set enUS language to recognize error message in the HTML source
		data = HTTPClient.connect(addressLanguage, cookieIdSession, log);
		debugPanel.addHtmlPage(data);
		checkAndThrowError(data, log);

		// Prepare the post data for the connection form
		postData = new PostData(
				PostElement.form("name", parametres.getLogin()), PostElement
						.form("password", parametres.getPassword()),
				PostElement.form("connect", 1));

		// Second connection to fill the form
		log.info("2 - "
				+ fillIdentificationForm.useAsFormater(addressConnection));
		data = HTTPClient.connect(addressConnection, cookieIdSession, postData,
				log);
		debugPanel.addHtmlPage(data);

		// if everything went well, the redirect address should be addressSend
		checkAndThrowError(data, log);

		if (!data.getAddress().equals(addressSend)) {
			log.error(invalidLoginOrPassword);
			log.error(identificationRejected);
			throw new RuntimeException(invalidLoginOrPassword.toString());
		}

		// At the send.php page. Change language to enUS
		data = HTTPClient.connect(addressLanguage, cookieIdSession, log);
		debugPanel.addHtmlPage(data);

		// Prepare the post data
		postData = new PostData(PostElement.form("LZProfiler", new File(
				parametres.getRosterFile())), PostElement.form("has_send_file",
				1));

		// Send the file and get the data results;
		log.info("3 - " + sendLZRosterData.useAsFormater(addressSend));
		log.info("       " + pleaseWait + "...");
		data = HTTPClient.connect(addressSend, cookieIdSession, postData, log);
		debugPanel.addHtmlPage(data);

		checkAndThrowError(data, log);

		List<String> characters = statusItemManager.extractUpdatedCharacters();

		if (characters.size() > 0) {

			log.info("       " + listOfUpdatedCharacters);
			int i = 0;
			for (String c : characters) {
				log.info(String.format("          %2d - %s", ++i, c));
			}
		} else {
			log.warning(noCharacterUpdated);
		}
		// data.getSource().dump(log, LoggerLevel.WARNING);
		log.info("4 - " + disconnetFromRoster);
		HTTPClient.connect(addressDisconnection, cookieIdSession, log);
		log.info("5 - " + updateCompleted);
	}

	public HTTPData getData() {
		return data;
	}

	private void checkAndThrowError(HTTPData data, Logger log) {
		statusItemManager = null;

		if (data.getStatus().isOk()) {

			statusItemManager = new StatusItemManager(data.getSource());

			if (!statusItemManager.isAnyCriticalItem())
				return;

			for (StatusItem si : statusItemManager) {
				if (si.isCritical()) {
					log.error(si.getCriticalInfo());
					throw new RuntimeException(si.getCriticalInfo().toString());
				}
			}

		}

		Exception e = data.getException();

		String message;
		if (e == null) {
			message = unknownError.toString();
		} else if (e instanceof MalformedURLException) {
			message = malformedUrlAddress
					.useAsFormater(e.getLocalizedMessage());
		} else if (e instanceof UnknownHostException) {
			message = unknownHost.useAsFormater(e.getMessage());
		} else if (e instanceof FileNotFoundException) {
			FileNotFoundException fnf = (FileNotFoundException) e;
			String exceptionMessage = fnf.getMessage();
			int idxBracket = exceptionMessage.indexOf("(") - 1;
			if (idxBracket > 0) {
				exceptionMessage = exceptionMessage.substring(0, idxBracket);
			}
			message = String.format("%s : '%s'", inputDataFileNotFound
					.toString(), exceptionMessage);
			;
		} else {
			message = String.format("Exception : %s   Message : '%s'", e, e
					.getMessage());
		}

		String errorMsg = errorWhenConnection.useAsFormater(message);
		log.error(errorMsg);
		throw new RuntimeException(errorMsg);
	}

	// public static Text update(Profile p, Logger log) {
	// Text result = null;
	// LZUpdater updater = new LZUpdater();
	// try {
	// updater.updateRoster(p, log);
	// } catch (Exception e) {
	// }
	// if (updater.data != null ) return updater.data.getSource();
	// return null;
	// }
}
